package fusion

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"math"
)

const (
	OPCODE_LARGE_PACKET = 0xffff
	OPCODE_NONE         = 0x0
)

const (
	LargePacketHeaderSize = 6
	LargePacketMaxSize    = math.MaxUint32
)

const (
	PacketHeaderSize    = 4
	PacketMaxSize       = math.MaxUint16
	PacketMaxBufferSize = PacketMaxSize - PacketHeaderSize
)

var (
	ErrPacketTooLarge   = errors.New("NetPacket: too large")
	ErrPacketCorruption = errors.New("NetPacket: corruption")
)

type NetPacket struct {
	Opcode int
	NetBuffer
}

func WriteNetPacketHeader(buffer *bytes.Buffer, opcode int, length int) {
	var packetSize = PacketHeaderSize + length
	if packetSize > PacketMaxSize {
		panic(ErrPacketTooLarge)
	}
	Must(binary.Write(buffer, defByteOrder, uint16(packetSize)))
	Must(binary.Write(buffer, defByteOrder, uint16(opcode)))
}

func ReadNetPacketHeader(data []byte) (opcode int, length int) {
	var buffer = bytes.NewBuffer(data)
	var packetSize uint16
	Must(binary.Read(buffer, defByteOrder, &packetSize))
	if packetSize < PacketHeaderSize {
		panic(ErrPacketCorruption)
	}
	var packetOpcode uint16
	Must(binary.Read(buffer, defByteOrder, &packetOpcode))
	opcode, length = int(packetOpcode), int(packetSize-PacketHeaderSize)
	return
}

func WriteLargeNetPacketHeader(buffer *bytes.Buffer, opcode int, length int) {
	var packetSize = LargePacketHeaderSize + length
	if packetSize > LargePacketMaxSize {
		panic(ErrPacketTooLarge)
	}
	Must(binary.Write(buffer, defByteOrder, uint32(packetSize)))
	Must(binary.Write(buffer, defByteOrder, uint16(opcode)))
}

func ReadLargeNetPacketHeader(data []byte) (opcode int, length int) {
	var buffer = bytes.NewBuffer(data)
	var packetSize uint32
	Must(binary.Read(buffer, defByteOrder, &packetSize))
	if packetSize < LargePacketHeaderSize {
		panic(ErrPacketCorruption)
	}
	var packetOpcode uint16
	Must(binary.Read(buffer, defByteOrder, &packetOpcode))
	opcode, length = int(packetOpcode), int(packetSize-LargePacketHeaderSize)
	return
}

func ReadNetPacketHeaderFromReader(r io.Reader) (opcode int, length int, err error) {
	var buffer [PacketHeaderSize]byte
	if _, err = io.ReadFull(r, buffer[:]); err == nil {
		opcode, length = ReadNetPacketHeader(buffer[:])
	}
	return
}

func GetOverflowNetPacketDataSize(length int) int {
	return ((PacketHeaderSize + length) & 0xffff) - PacketHeaderSize
}
